﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Deferred.Library.Configuration
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="newOption"></param>
    public delegate void OptionAddedEvent(Option newOption);

    /// <summary>
    /// 
    /// </summary>
    public interface IOptions
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        /// <param name="Value"></param>
        void AddOption(string FullName, bool Value);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        /// <param name="Value"></param>
        /// <param name="Callback"></param>
        void AddOption(string FullName, bool Value, OptionCallback Callback);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="option"></param>
        void AddOption(Option option);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        /// <returns></returns>
        bool this[string FullName] { get; set; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        void Toggle(string FullName);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Category"></param>
        /// <returns></returns>
        List<Option> GetOptions(string Category);
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        List<Option> GetOptions();
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        List<string> GetCategories();
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        /// <returns></returns>
        bool OptionExists(string FullName);
        /// <summary>
        /// 
        /// </summary>
        event OptionAddedEvent OptionAdded;
    }

    /// <summary>
    /// 
    /// </summary>
    public class Options : IOptions
    {
        private List<string> categories = new List<string>();
        private List<Option> options = new List<Option>();
        private Dictionary<string, List<Option>> categoryToOptionList = new Dictionary<string, List<Option>>();
        private Dictionary<string, Option> fullNameToOption = new Dictionary<string, Option>();

        /// <summary>
        /// 
        /// </summary>
        public event OptionAddedEvent OptionAdded = delegate { };

        /// <summary>
        /// 
        /// </summary>
        /// <param name="serviceProvider"></param>
        public Options(IServiceProvider serviceProvider)
        {
            ICommandLineEngine commandLine = (ICommandLineEngine)serviceProvider.GetService(typeof(ICommandLineEngine));
            if (commandLine != null)
            {
                commandLine.AddCommand("setOptionValue", "Sets the value of an option",
                    delegate(string[] arguments)
                    {
                        bool value;

                        if (3 != arguments.Length) return "Invalid number of arguements";
                        if (!Boolean.TryParse(arguments[2], out value)) return "Parameter was not a boolean";
                        if (!OptionExists(arguments[1])) return "Invalid option name";
                        this[arguments[1]] = value;

                        return null;
                    }
                );

                commandLine.AddCommand("getOptionValue", "Gets the value of an option",
                    delegate(string[] arguments)
                    {
                        if (2 != arguments.Length) return "Invalid number of arguements";
                        if (!OptionExists(arguments[1])) return "Invalid option name";
                        return this[arguments[1]].ToString();
                    }
                );

                commandLine.AddCommand("getOptions", "Displays a list of all the available options",
                    delegate(string[] arguments)
                    {
                        StringBuilder builder = new StringBuilder();
                        foreach (Option option in GetOptions())
                        {
                            builder.Append("\r\n");
                            builder.Append(option.FullName);
                        }
                        builder.Remove(0, 2);
                        return builder.ToString();
                    }
                );
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        /// <param name="Value"></param>
        public void AddOption(string FullName, bool Value)
        {
            AddOption(new Option(FullName, Value));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        /// <param name="Value"></param>
        /// <param name="Callback"></param>
        public void AddOption(string FullName, bool Value, OptionCallback Callback)
        {
            AddOption(new Option(FullName, Value, Callback));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="option"></param>
        public void AddOption(Option option)
        {
            if (!fullNameToOption.ContainsKey(option.FullName))
            {
                fullNameToOption.Add(option.FullName, option);

                if (!categories.Contains(option.Category))
                {
                    categories.Add(option.Category);
                    categoryToOptionList.Add(option.Category, new List<Option>());
                }
                categoryToOptionList[option.Category].Add(option);
                options.Add(option);
                OptionAdded(option);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        /// <returns></returns>
        public bool this[string FullName]
        {
            get
            {
                return fullNameToOption[FullName].Value;
            }
            set
            {
                if (fullNameToOption.ContainsKey(FullName))
                {
                    fullNameToOption[FullName].Value = value;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        public void Toggle(string FullName)
        {
            this[FullName] = !this[FullName];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Category"></param>
        /// <returns></returns>
        public List<Option> GetOptions(string Category)
        {
            return new List<Option>(categoryToOptionList[Category]);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<Option> GetOptions()
        {
            return new List<Option>(options);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<string> GetCategories()
        {
            return new List<string>(categories);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="FullName"></param>
        /// <returns></returns>
        public bool OptionExists(string FullName)
        {
            return fullNameToOption.ContainsKey(FullName);
        }
    }
}
